package cn.like.satoken.jwt;

import cn.dev33.satoken.SaManager;
import cn.dev33.satoken.secure.SaSecureUtil;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.CharUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import cn.hutool.jwt.JWT;
import cn.hutool.jwt.signers.AlgorithmUtil;
import cn.hutool.jwt.signers.JWTSigner;
import cn.hutool.jwt.signers.JWTSignerUtil;
import cn.like.base.auth.UserInfo;
import cn.like.base.support.exception.BizException;
import cn.like.redis.RedisKey;
import cn.like.util.RsaHelper;
import org.springframework.stereotype.Component;

import java.security.KeyPair;
import java.util.Date;
import java.util.List;

/**
 * JSON WEB TOKEN 管理
 * <pre>
 *     1.创建token
 *     2.根据token获取用户信息
 * </pre>
 *
 * @author like 980650920@qq.com
 * @date 2021/7/16 8:13
 */
@Component
public class JwtManager {

    private static final String KEY_TEMP_USER = "TEMP_USER:{0}";
    private final JWTSigner signer;
    private final RsaHelper rsaHelper;
    private final String secret = "zxljfkashqwem";

    public JwtManager(RsaHelper rsaHelper) {
        this.rsaHelper = rsaHelper;
        this.signer = JWTSignerUtil.createSigner(
                AlgorithmUtil.getAlgorithm("RS256"),
                new KeyPair(rsaHelper.getRsa().getPublicKey(), rsaHelper.getRsa().getPrivateKey()));
    }

    /**
     * 创建令牌。
     * <pre>
     *     前提： redis中必须存在 TEMP_USER:{userId}:userinfo
     * </pre>
     *
     * @param loginId 登录id
     * @return {@link String} token
     */
    public String create(Object loginId) {
        // 获取缓存在redis中的用户信息
        final String key = RedisKey.of(KEY_TEMP_USER,loginId);
        final UserInfo user = JSONUtil.toBean(SaManager.getSaTokenDao().get(key), UserInfo.class);
        SaManager.getSaTokenDao().delete(key);

        // create token
        return create(user);
    }

    /**
     * 创建令牌
     * <pre>
     *     根据用户信息创建token
     * </pre>
     *
     * @param user 用户信息
     * @return {@link String} token
     */
    public String create(UserInfo user) {
        String token = JWT.create().setSigner(signer)
                .setPayload("id", user.getId())
                .setPayload("account", user.getAccount())
                .setPayload("orgId", user.getOrgId())
                .setPayload("stationId", user.getStationId())
                .setPayload("email", user.getEmail())
                .setPayload("mobile", user.getMobile())
                .setPayload("sex", user.getSex())
                .setPayload("workDescribe", user.getWorkDescribe())
                .setPayload("avatar", user.getAvatar())
                .setPayload("status", user.getStatus())
                .setIssuedAt(new Date())
                .setExpiresAt(DateUtil.date(new Date().getTime() + SaManager.getConfig().getTimeout()))
                .setSubject(user.getName())
                .setIssuer("like").sign();

        // 对payload加密
        token = encryptPayload(token);

        return token;
    }

    /**
     * 验证 token 是否被修改了
     *
     * @param token 需要验证的令牌
     * @return boolean
     */
    public boolean verify(String token){
        return JWT.of(token).verify(signer);
    }

    /**
     * 使一个token过期
     *
     * @param token 被移除的令牌
     */
    public void letExpire(String token) {
        StpUtil.logoutByTokenValue(token);
    }

    /**
     * 使一个用户过期
     *
     * @param userId 用户id
     */
    public void letExpire(Object userId) {
        StpUtil.logoutByLoginId(userId);
    }

    /**
     * 获取用户信息
     *
     * @return {@link UserInfo}
     */
    public UserInfo getUser() {
        return this.getUser(StpUtil.getTokenValue());
    }

    /**
     * 根据token获取用户信息
     * <pre>
     *     不用担心接受到过期的token，拦截器已经做好了
     * </pre>
     *
     * @param token 令牌
     * @return {@link UserInfo}
     */
    public UserInfo getUser(String token) {
        // 对payload解密
        token = decryptPayload(token);

        final JWT jwt = JWT.of(token);
        if (!jwt.verify(signer)) throw new BizException("当前token无效:" + token);

        return JSONUtil.toBean(jwt.getPayloads(), UserInfo.class);
    }

    // ====================================================================   private methods

    private String encryptPayload(String token) {
        final List<String> tokens = StrUtil.split(token, CharUtil.DOT);

        return StrUtil.format("{}.{}.{}", tokens.get(0), SaSecureUtil.aesEncrypt(secret, tokens.get(1)), tokens.get(2));
    }

    private String decryptPayload(String token) {
        final List<String> tokens = StrUtil.split(token, CharUtil.DOT);

        return StrUtil.format("{}.{}.{}", tokens.get(0), SaSecureUtil.aesDecrypt(secret, tokens.get(1)), tokens.get(2));
    }

}
